#ifndef _HH_OBJECTSAXPARSER
#define _HH_OBJECTSAXPARSER
/**
 * \file ObjectSaxParser.hh (c) 2011 by Andrew E Page
 *
 *
 * \mainpage
 *
 *   Object connector for libxml2 parser
 *
\code
class DOCParser {
public:

    void handleStart(const string& tag) ;
    void handleEnd(const string& tag, const string& data, const attrDict& attrs) ;
};

void DOCParser::handleStart(const string& tag)
{
    cout << "Starting " << tag << endl ;
}

void DOCParser::handleEnd(const string& tag, const string& data, const attrDict& attrs)
{
    cout << "Ending " << tag << endl ;

    for( attrDict::const_iterator it = attrs.begin() ; it != attrs.end() ; it++) {
        cout << "  " << it->first << ":  " << it->second << endl ;
    }

    cout << data << endl ;

}


int main(int arch, char **argv)
{
    ObjectSAXParser parser;
    DOCParser handlers ;

    // Add a handler to handle the start of the DOC tag
    parser.addStartHandler("DOC", h(&handlers, &DOCParser::handleStart)) ;
    
    // Add a handler to handle the closing of a node tag or a 
    // <node/> tag
    parser.addEndHandler("node", h(&handlers, &DOCParser::handleEnd)) ;

    
    // parse the file
    
    parser.parse(argv[1]) ;

}
\endcode

 */
#include <libxml/parser.h>

#include <string>
#include <list>
#include <tr1/unordered_map>

/**
 * \namespace
 *
 *  Objects and functions used in the objectsaxparser
 */
namespace objectsaxparser {


/**
 * Dictionary type for attributes
 */
typedef std::tr1::unordered_map<std::string, std::string>  attrDict ;

/**
 * Interface class for handlers that handle the start of tags
 * 
 * @author aepage (4/17/2011)
 */
class ITagStart {
    typedef  std::string string ;

public:
  /** 
   * invoked by the parser with the tag seen when it starts
   */
    virtual void tagStart(const string& tag) = 0 ;

};

/**
 * Interface class for handlers that handle the closing of tags
 * 
 * @author aepage (4/17/2011)
 */
class ITagEnd {
public:
  /**
   * invoked by the parser when registered 
   *
   *  @param   tag seen by the parser
   *  @param   data data collected between openning and closing of  the tag
   *           Example:  &lt;tag attr1="value1" attr2="value2"&gt;this is data&lt;/tag&gt;<BR>
   *           when parsed data="this is data", attrDict = { 'attr1': 'value1', 'attr2': 'value2' }
   *
   */
    virtual void tagEnd(const std::string& tag, const std::string& data, const attrDict& attrs) = 0 ;

};

/**
 * Delegate helper object that can connect and object/method 
 * pair as a handler for a start tag 
 * 
 * @author aepage (4/17/2011)
 */
template <class T>
class StartDelegate : public ITagStart {
    typedef  std::string string ;
public:
    typedef void (T::*delegate_method)(const string& tag) ;
    
    StartDelegate(T *delegate, delegate_method method) ;
    StartDelegate(const StartDelegate<T>& copy) ;

    /**
     * Overridden from ITagStart.   Invokes the method on the object 
     * specified in the constructor upong seeing the registered tag 
     * 
     * @author aepage (4/19/2011)
     */
    virtual void tagStart(const string& tag) ;
    
private:
    T *delegate ;
    delegate_method const method ;

};

/**
 * Delegate helper object that can connect and object/method 
 * pair as a handler for a tag ending
 * 
 * @author aepage (4/17/2011)
 */
template <class T>
class EndDelegate : public ITagEnd {
    typedef std::string string ;
public:
    typedef void (T::*delegate_method)(const string& tag, const string &data, const attrDict& attrs) ;

    EndDelegate(T * delegate, delegate_method method) ;
    EndDelegate(const EndDelegate<T>& copy) ;

    virtual void tagEnd(const string &tag, const string &data, const attrDict &attrs) ;
private:
    T *delegate ;
    delegate_method method ;
};

  /**
   * Helper function that generates the appropriate delegate.
   * <BR>
   * <BR>
   * Example:<BR><BR> 
   *  \code
   *   ObjectSAXParser parser ;
   *   parser.addEndHandler("tag", h(myObj, &myObj::tagMethod)) ;
   * \endcode
   *  @param obj    object that will be responsible for processing the start tag
   *  @param method method within object that will do the processing
   */
template<class T>
inline ITagStart *h(T *obj, void (T::*method)(const std::string& tag))
{
    return new StartDelegate<T>(obj, method) ;
}

  /**
   * Helper function that generates the appropriate delegate.
   * <BR>
   * <BR>
   * Example:<BR><BR> 
   *  \code
   *   ObjectSAXParser parser ;
   *   parser.addStartHandler("tag", h(myObj, &myObj::tagMethod)) ;
   * \endcode
   *  @param obj    object that will be responsible for processing the start tag
   *  @param method method within object that will do the processing
   */
template<class T>
inline ITagEnd *h(T *obj, void (T::*method)(const std::string& tag, const std::string& data, const attrDict& attrs))
{
    return new EndDelegate<T>(obj, method) ;
}

/**
 * Parser that connects tags, their attributes and data with 
 * objects and methods to process them 
 * 
 * @author aepage (4/17/2011)
 */
class ObjectSAXParser {
    typedef std::string string ;

public:
    ObjectSAXParser() ;
    virtual ~ObjectSAXParser() ;

    /**
     * Parse the Document specified in fName
     * 
     * @author aepage (4/22/2011)
     * 
     * @param fName path to the file to be parsed
     * 
     * @return int result from xmlSAXUserParseFile
     */
    int parse(const string& fName) ;

    /**
     * Resets the parser.  Releases any resources currently held. 
     * Should NOT be called while the parsing is in progress. 
     * 
     * @author aepage (4/22/2011)
     */
    void reset() ;

  /**
   * Adds a handler for the start of the specified tag
   * @param tag   tag to invoke handler for
   * @param handler handler for tag
   */
    void addStartHandler(const string& tag, ITagStart *handler) ;
  /**
   * Adds a handler for the end of the specified tag
   *
   * @param tag  tag to invoke handler for
   * @param handler for tag
   */
    void addEndHandler(const string& tag, ITagEnd *handler) ;

protected:
  virtual void startDocument() ;
  virtual void endDocument() ;
  virtual void vwarning(const char *msg, va_list args) ;
  virtual void verror(const char *msg, va_list args) ;
  virtual void characters(const xmlChar *chars, int len) ;

private: 

  /**
   * record of each tag level we pass through
   */
    class parserStackEntry {
    public:
        attrDict dict ;
        xmlChar  *data ;
        int       data_length ;
        parserStackEntry() : data(NULL), data_length(0) { }
        virtual ~parserStackEntry() ;
    };

    typedef std::list<parserStackEntry> parserStack_t ; 
    typedef std::list<ITagStart *> start_handler_list_t ;
    typedef std::list<ITagEnd *> end_handler_list_t ;

    typedef std::tr1::unordered_map<string, start_handler_list_t> startHandlerMap_t ;
    typedef std::tr1::unordered_map<string, end_handler_list_t> endHandlerMap_t ;


    static void _startDocument(void *ctx) ;            //!< start of document handler routine for libxml

    static void _endDocument(void *ctx) ;

    static void _characters(void *ctx, 
                           const xmlChar *chars, 
                           int len) ;

    static void startElement(void *ctx, 
                             const xmlChar *fullName, 
                             const xmlChar **atts) ;
    static void elementEnd(void *ctx, 
                           const xmlChar *) ;

    static void warning(void *, const char *msg, ...) ;
    static void error(void *, const char *msg, ...) ;

    xmlChar *getBuffer() ;

    startHandlerMap_t startHandlerMap ;
    endHandlerMap_t endHandlerMap ;
    parserStack_t parserStack ;

};



}


#endif // _HH_OBJECTSAXPARSER
